﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;

namespace GBricks
{
  using Properties;

  [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Optional", Justification = Justifications.ConvenientIdentifier)]
  public struct Optional<TValue> : IEquatable<Optional<TValue>>
  {
    private const int HashCodeMagicNumber = 56971;

    private readonly TValue value;
    private readonly bool hasValue;

    public Optional(TValue value) {
      this.value = value;
      hasValue = true;
    }

    public bool HasValue {
      [DebuggerStepThrough]
      get { return hasValue; }
    }

    public TValue Value {
      [DebuggerStepThrough]
      get {
        Invariant.Requires(HasValue, Resources.OptionalDoesNotHasValue);
        return value;
      }
    }

    public override bool Equals(object obj) {
      return obj is Optional<TValue> && Equals((Optional<TValue>)obj);
    }

    public override int GetHashCode() {
      return HasValue ? EqualityComparer<TValue>.Default.GetHashCode(Value) : HashCodeMagicNumber;
    }

    public override string ToString() {
      return HasValue ? Convert.ToString(Value, null) : String.Empty;
    }

    #region IEquatable<Optional<T>> Members

    public bool Equals(Optional<TValue> other) {
      if(HasValue == other.HasValue) {
        return !HasValue || EqualityComparer<TValue>.Default.Equals(Value, other.Value);
      }//if

      return false;
    }

    #endregion IEquatable<Optional<T>> Members

    public static bool operator ==(Optional<TValue> left, Optional<TValue> right) {
      return left.Equals(right);
    }

    public static bool operator !=(Optional<TValue> left, Optional<TValue> right) {
      return !(left == right);
    }

    public static implicit operator Optional<TValue>(TValue value) {
      return new Optional<TValue>(value);
    }

    public static explicit operator TValue(Optional<TValue> value) {
      return value.Value;
    }
  }
}
